package cn.keniykey.mc.landterritory.landflag;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.annotation.Nullable;

import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;

import cn.keniykey.mc.landterritory.LandTerritoryMain;
import cn.keniykey.mc.landterritory.config.ConfigurationManager;
import cn.keniykey.mc.landterritory.land.api.ELandType;
import cn.keniykey.mc.landterritory.landflag.api.ELandFlagParType;
import cn.keniykey.mc.landterritory.landflag.api.ELandFlagType;
import cn.keniykey.mc.landterritory.landflag.api.JavaLandFlag;
import cn.keniykey.mc.landterritory.landflag.api.LandFlagListenerManager;
import cn.keniykey.mc.landterritory.landflag.api.LandFlagScheduler;
import cn.keniykey.mc.landterritory.landflag.api.LandFlagSetting;
import cn.keniykey.mc.landterritory.landflag.flag.LandAdminFlag;
import cn.keniykey.mc.landterritory.toolkit.DebugUtil;
import cn.keniykey.mc.landterritory.toolkit.PackageUtil;
import cn.keniykey.mc.viewlibrary.toolkit.VerAnalysisUtil;

public final class LandFlagManager {
	public static final int API_VERSION = 4;
	private static LandFlagManager manager = new LandFlagManager();
	
	public static LandFlagManager getFlagManager() {
		return manager;
	}
	
	private HashMap<String,JavaLandFlag> hashmap_name_flags = new HashMap<>();
	private LandFlagLoader loader_flag = new LandFlagLoader();
	
	public ArrayList<LandFlag> getLandFlags(ELandFlagType type){
		final LinkedList<LandFlag> flags = new LinkedList<>();
		for(LandFlag flag : this.hashmap_name_flags.values()) {
			if(flag.getELandFlagType().equals(type)) flags.add(flag);
		}
		try {
			return new ArrayList<>(flags);
		}finally {
			flags.clear();
		}
	}
	@Nullable
	public LandFlag getLandFlag(String name) {
		return (hashmap_name_flags.containsKey(name) ? hashmap_name_flags.get(name) : null);
	}
	public Collection<LandFlag> getLandFlags() {
		return Collections.unmodifiableCollection(hashmap_name_flags.values());
	}
	public HashMap<String, Set<String>> getInitialLandFlags(ELandType type){
		List<String> list;
		try {
			switch(type) {
			case COMMUNITY:
				list = ConfigurationManager.Community_Create_Flags;
				break;
			case GROUND:
				list = ConfigurationManager.Ground_Create_Flags;
				break;
			case TERRITORY:
				list = ConfigurationManager.Territory_Create_Flags;
				break;
			default:
				list = Collections.emptyList();
			}
		}catch(Exception ex) {
			ex.printStackTrace();
			list = Collections.emptyList();
		}
		final HashMap<String, Set<String>> flags = new HashMap<>();
		if(list.isEmpty()) return flags;
		try {
			for(String flagstr : list) {
				final String[] array_str = flagstr.split(":");
				if(!hashmap_name_flags.containsKey(array_str[0])) continue;
				final int par_amount = (array_str.length - 1);
				final Set<String> flagpars;
				if (par_amount > 1) {
					flagpars = new HashSet<>(par_amount);
					for(int index = 0; index <  par_amount; index++) flagpars.add(array_str[index]);
				}else {
					flagpars = Collections.emptySet();
				}
				flags.put(array_str[0], flagpars);
			}
		}catch(Exception ex) {
			ex.printStackTrace();
		}
		return flags;
	}
	public void loadFlags() {
		final File savepath = new File(LandTerritoryMain.getPlugin().getDataFolder(),"landflag");
		if(savepath.exists()) {
			if(savepath.isFile()) {
				savepath.delete();
				savepath.mkdirs();
			}
		}else {
			savepath.mkdirs();
		}
		final File[] flagfiles = savepath.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".jar");
			}
		});
		this.loadPrivateFlags();
		for(final File flagfile : flagfiles) {
			if(!flagfile.isFile()) continue; //不是文件忽略
			if(!loadFlag(flagfile)) {
				LandTerritoryMain.getPlugin().getLogger().warning("领地标记: " + flagfile.getName() + " 文件无法加载");
			}
		}
	}
	private void loadPrivateFlags() {
		final LinkedList<String> link_flags_loc = PackageUtil.getClassName(LandAdminFlag.class.getPackage().getName());
		while(!link_flags_loc.isEmpty()) {
			try {
				final Class<?> clazz;
				try {
					clazz = Class.forName(link_flags_loc.removeFirst());
				}catch(Exception ex) {
					continue;
				}
				if(!JavaLandFlag.class.isAssignableFrom(clazz)) continue;
				final JavaLandFlag flag = (JavaLandFlag) clazz.getConstructor().newInstance();
				if(flag instanceof IBuiltInLandFlag) {
					((LandFlag)flag).init(((IBuiltInLandFlag)flag).getBuiltInDescription(), null);
				}else {
					String flagname = clazz.getSimpleName();
					if(flagname.endsWith("Flag")) flagname = flagname.substring(0, (flagname.length()-4)); 
					((LandFlag)flag).init(new LandFlagDescription(clazz.getName(), flagname), null);
				}
				loadFlag(flag);
			}catch(Exception ex) {
				ex.printStackTrace();
			}
		}
	}
	public boolean registerLandFlag(String flagname, JavaLandFlag flag, int version, Set<String> conflict_flags) {
		if(flag.getPlugin() == null) return false;
		if(hashmap_name_flags.containsKey(flagname)) { //如果出现加载相同的标记，version最高的保留
			final LandFlag theflag = hashmap_name_flags.get(flagname);
			if(version <= theflag.getDescription().getVersion()) return false;
			unloadFlag(theflag);
			LandTerritoryMain.getPlugin().getLogger().warning("发现高版本领地标记 " + flagname + " ，已停用低版本");
		}
		((LandFlag)flag).init(new LandFlagDescription(flag.getClass().getName(), flagname, version, conflict_flags), null);
		return loadFlag(flag);
	}
	private boolean loadFlag(JavaLandFlag flag) {
		final ELandFlagParType type = flag.getELandFlagParType();
		if(type == null) throw new NullPointerException("[LandFlagManager]领地标记加载指令出错：ELandFlagType为NULL");
		hashmap_name_flags.put(flag.getName(), flag);
		((LandFlag)flag).bool_loaded = true;
		try {
			flag.onEnable();
		}catch(Exception ex) {
			ex.printStackTrace();
			DebugUtil.info("领地标记加载[LandFlagManager]JavaLandFlag出错：领地标记的自定义onFlagLoad出错，");
			unloadFlag(flag);
			return false;
		}
		return true;
	}
	public boolean loadFlag(File file_flag) {
		final LandFlagDescription des;
		try{
			des = loader_flag.loadLandFlagDescription(file_flag);
		}catch(Exception ex) {
			ex.printStackTrace();
			DebugUtil.info("领地标记加载指令[LandFlagManager]出错：无法加载Description");
			return false;
		}
		if(des.getAPIVersion() > API_VERSION) {
			LandTerritoryMain.getPlugin().getLogger().warning("领地标记 " + des.getName() + " 要求最低APIVersion为 " + des.getAPIVersion());
			return false;
		}
		if(!des.getPrepositionPlugin().isEmpty()) {
			for(final String str : des.getPrepositionPlugin()) {
				if(Bukkit.getPluginManager().getPlugin(str) != null) continue;
				LandTerritoryMain.getPlugin().getLogger().warning("领地标记 " + des.getName() + " 缺少前置插件 " + str);
				return false;
			}
		}
		if(VerAnalysisUtil.isBefore(des.getGameVersion())) {
			LandTerritoryMain.getPlugin().getLogger().warning("领地标记 " + des.getName() + " 要求最低游戏版本为 " + des.getGameVersion());
			return false;
		}
		if(hashmap_name_flags.containsKey(des.getName())) { //如果出现加载相同的标记，version最高的保留
			final LandFlag flag = hashmap_name_flags.get(des.getName());
			if(des.getVersion() <= flag.getDescription().getVersion()) return false;
			unloadFlag(flag);
			LandTerritoryMain.getPlugin().getLogger().warning("发现高版本领地标记 " + des.getName() + " ，已停用低版本");
		}
		final LandFlagClassLoader classloader;
		try{
			classloader = loader_flag.loadLandFlag(file_flag, des);
		}catch(Exception ex) {
			ex.printStackTrace();
			DebugUtil.info("领地标记加载指令[LandFlagManager]File出错：无法启动ClassLoader");
			return false;
		}
		final JavaLandFlag flag = classloader.getFlag();
		if(flag == null) {
			try {
				classloader.close();
			}catch(Exception ex) {
				ex.printStackTrace();
				DebugUtil.info("领地标记加载指令[LandFlagManager]出错：无法关闭ClassLoader");
			}
			DebugUtil.info("领地标记加载指令[LandFlagManager]出错：获取到了空的LandFlag");
			return false;
		}
		return loadFlag(flag);
	}
	public void unloadFlag(LandFlag flag) {
		hashmap_name_flags.remove(flag.getDescription().getName());
		if(flag instanceof JavaLandFlag) {
			try {
				((JavaLandFlag)flag).onDisable();
			}catch(Exception ex) {
				ex.printStackTrace();
			}
		}
		LandFlagSetting.reset(flag.getName());
		LandFlagListenerManager.unRegisterListener(flag);
		LandFlagScheduler.cancelTask(flag);
		flag.bool_loaded = false;
		if(flag.loader != null) {
			try {
				flag.loader.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}
	public void unloadFlags(Plugin plugin) {
		final LinkedList<LandFlag> linked_flags = new LinkedList<>(hashmap_name_flags.values());
		while(!linked_flags.isEmpty()) {
			final LandFlag flag = linked_flags.removeFirst();
			if(!(flag instanceof JavaLandFlag)) continue;
			final JavaLandFlag javaflag = (JavaLandFlag)flag;
			if(javaflag.getPlugin() == null || !javaflag.getPlugin().equals(plugin)) continue;
			unloadFlag(flag);
		}
	}
	public void unloadFlags() {
		if(hashmap_name_flags.isEmpty()) return;
		final LinkedList<LandFlag> linked_flags = new LinkedList<>(hashmap_name_flags.values());
		hashmap_name_flags.clear();
		while(!linked_flags.isEmpty()) {
			final LandFlag flag = linked_flags.removeFirst();
			if(flag instanceof JavaLandFlag) {
				try {
					((JavaLandFlag)flag).onDisable();
				}catch(Exception ex) {
					ex.printStackTrace();
				}
			}
			LandFlagSetting.reset(flag.getName());
			LandFlagListenerManager.unRegisterListener(flag);
			LandFlagScheduler.cancelTask(flag);
			flag.bool_loaded = false;
			if(flag.loader != null) {
				try {
					flag.loader.close();
				} catch (Exception ex) {
					ex.printStackTrace();
				}
			}
		}
	}
}